#pragma once

#include <functional>
#include <chrono>
#include <future>
#ifndef NOT_USE_THREAD_POOL
#include <boost/asio.hpp>
// ref: https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/thread_pool.html
class ThreadPoolSingleton
{
public:
	static ThreadPoolSingleton& instance()
	{
		static ThreadPoolSingleton ins;
		return ins;
	}
	boost::asio::thread_pool& pool() { return _pool; }
	void print()
	{
		// TODO print the threads status
	}

private:
	ThreadPoolSingleton() :_pool(4) {} // TODO 
	~ThreadPoolSingleton() = default;
	boost::asio::thread_pool _pool;
};
#endif

// ref: https://stackoverflow.com/questions/14650885/how-to-create-timer-events-using-c-11

// Launch the pool with four threads.
class later
{
public:
	template <class callable, class... arguments>
	later(int after, bool async, callable&& f, arguments&&... args)
	{
		std::function<typename std::result_of<callable(arguments...)>::type()> task(std::bind(std::forward<callable>(f), std::forward<arguments>(args)...));

		if (async)
		{
#ifdef NOT_USE_THREAD_POOL	// not use thread pool
			std::thread([after, task]() {
				std::this_thread::sleep_for(std::chrono::milliseconds(after));
				task();
			}).detach();
#else
			// static boost::asio::thread_pool s_threadpool(1);
			auto& pool = ThreadPoolSingleton::instance().pool();
			boost::asio::post(pool, [after, task]() {
				std::this_thread::sleep_for(std::chrono::milliseconds(after));
				task();
			});
#endif
		}
		else
		{
			std::this_thread::sleep_for(std::chrono::milliseconds(after));
			task();
		}
	}
	~later() = default;
private:
	later() = delete;
};
